﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ManagedCuda.BasicTypes;
using ManagedCuda;
using ManagedCuda.CudaBlas;
using MathNet.Numerics.LinearAlgebra.Single;
using System.Reflection;
using MathNet.Numerics.LinearAlgebra.Generic;

namespace cudaMatrix
{
    enum Direction { ToHost, ToDevice };

    public class CudaMatrix : DenseMatrix
    {
        internal CudaDeviceVariable<float> device;
        internal CudaBlas cublas;

        public CudaMatrix(int rows, int columns, float[] array)
            : base(rows, columns, array)
        {
            cublas = BLASProvider.Instance;
            device = Data;
        }

        public CudaMatrix(int rows, int columns, float value)
            : base(rows, columns, value)
        {
            cublas = BLASProvider.Instance;
            device = Data;
        }

        internal void sync(Direction dir)
        {
            if (dir == Direction.ToDevice)
            {
                this.device = this.Data;
            }
            else if (dir == Direction.ToHost)
            {
                System.Diagnostics.Trace.WriteLine("tohost: " + device[0]);
                Data = device;
                System.Diagnostics.Trace.WriteLine("host: " + Data[0]);
                At(1, 1, 1.0f);
            }
        }

        protected override void DoMultiply(Matrix<float> other, Matrix<float> result)
        {
            System.Diagnostics.Trace.WriteLine("CUDA MATRIX MULTIPLICATION");
            var denseOther = other as CudaMatrix;
            CudaMatrix forMult = null;
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            var tmp = result as DenseMatrix;
            forMult = MatrixFactory.createMatrix(tmp.RowCount, tmp.ColumnCount, tmp.Data) as CudaMatrix;
            sw.Start();
            sync(Direction.ToDevice);
            sw.Stop();
            System.Diagnostics.Trace.WriteLine("Elapsed CUDA Copy1=" + sw.Elapsed);
            sw.Start();
            denseOther.sync(Direction.ToDevice);
            sw.Stop();
            System.Diagnostics.Trace.WriteLine("Elapsed CUDA Copy2=" + sw.Elapsed);
            sw.Start();
            cublas.Gemm(Operation.NonTranspose, Operation.NonTranspose, RowCount, denseOther.ColumnCount, ColumnCount, 1.0f, device, RowCount, denseOther.device, denseOther.RowCount, 1.0f, forMult.device, tmp.RowCount);
            sw.Stop();
            System.Diagnostics.Trace.WriteLine("Elapsed CUDA Mult=" + sw.Elapsed);
            sw.Start();
            tmp.Data = forMult.device;
            System.Diagnostics.Trace.WriteLine("after: " + tmp.Data[0]);
            sw.Stop();
            System.Diagnostics.Trace.WriteLine("Elapsed CUDA Copy3=" + sw.Elapsed);
        }
    }
}
